home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / OLE / OLE.php next >
PHP Script  |  2003-12-14  |  14KB  |  411 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2002 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license,      |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Xavier Noguer <xnoguer@php.net>                              |
  17. // | Based on OLE::Storage_Lite by Kawai, Takanori                        |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: OLE.php,v 1.7 2003/08/21 15:15:40 xnoguer Exp $
  21.  
  22.  
  23. /**
  24. * Constants for OLE package
  25. */
  26. define('OLE_PPS_TYPE_ROOT',        5);
  27. define('OLE_PPS_TYPE_DIR',         1);
  28. define('OLE_PPS_TYPE_FILE',        2);
  29. define('OLE_DATA_SIZE_SMALL', 0x1000);
  30. define('OLE_LONG_INT_SIZE',        4);
  31. define('OLE_PPS_SIZE',          0x80);
  32.  
  33. require_once('PEAR.php');
  34. require_once 'OLE/PPS.php';
  35.  
  36. /**
  37. * OLE package base class.
  38. *
  39. * @author   Xavier Noguer <xnoguer@php.net>
  40. * @category Structures
  41. * @package  OLE
  42. */
  43. class OLE extends PEAR
  44. {
  45.     /**
  46.     * The file handle for reading an OLE container
  47.     * @var resource
  48.     */
  49.     var $_file_handle;
  50.  
  51.     /**
  52.     * Array of PPS's found on the OLE container
  53.     * @var array
  54.     */
  55.     var $_list;
  56.  
  57.     /**
  58.     * Creates a new OLE object
  59.     * Remember to use ampersand when creating an OLE object ($my_ole =& new OLE();)
  60.     * @access public
  61.     */
  62.     function OLE()
  63.     {
  64.         $this->_list = array();
  65.     }
  66.  
  67.     /**
  68.     * Reads an OLE container from the contents of the file given.
  69.     *
  70.     * @acces public
  71.     * @param string $file
  72.     * @return mixed true on success, PEAR_Error on failure
  73.     */
  74.     function read($file)
  75.     {
  76.         /* consider storing offsets as constants */
  77.         $big_block_size_offset = 30;
  78.         $iBdbCnt_offset        = 44;
  79.         $bd_start_offset       = 68;
  80.  
  81.         $fh = @fopen($file, "r");
  82.         if ($fh == false) {
  83.             return $this->raiseError("Can't open file $file");
  84.         }
  85.         $this->_file_handle = $fh;
  86.  
  87.         /* begin reading OLE attributes */
  88.         fseek($fh, 0);
  89.         $signature = fread($fh, 8);
  90.         if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) {
  91.             return $this->raiseError("File doesn't seem to be an OLE container.");
  92.         }
  93.         fseek($fh, $big_block_size_offset);
  94.         $packed_array = unpack("v", fread($fh, 2));
  95.         $big_block_size   = pow(2, $packed_array['']);
  96.  
  97.         $packed_array = unpack("v", fread($fh, 2));
  98.         $small_block_size = pow(2, $packed_array['']);
  99.         $i1stBdL = ($big_block_size - 0x4C) / OLE_LONG_INT_SIZE;
  100.  
  101.         fseek($fh, $iBdbCnt_offset);
  102.         $packed_array = unpack("V", fread($fh, 4));
  103.         $iBdbCnt = $packed_array[''];
  104.  
  105.         $packed_array = unpack("V", fread($fh, 4));
  106.         $pps_wk_start = $packed_array[''];
  107.  
  108.         fseek($fh, $bd_start_offset);
  109.         $packed_array = unpack("V", fread($fh, 4));
  110.         $bd_start = $packed_array[''];
  111.         $packed_array = unpack("V", fread($fh, 4));
  112.         $bd_count = $packed_array[''];
  113.         $packed_array = unpack("V", fread($fh, 4));
  114.         $iAll = $packed_array[''];  // this may be wrong
  115.         /* create OLE_PPS objects from */
  116.         $ret = $this->_readPpsWks($pps_wk_start, $big_block_size);
  117.         if (PEAR::isError($ret)) {
  118.             return $ret;
  119.         }
  120.         return true;
  121.     }
  122.  
  123.     /**
  124.     * Destructor (using PEAR)
  125.     * Just closes the file handle on the OLE file.
  126.     *
  127.     * @access private
  128.     */
  129.     function _OLE()
  130.     {
  131.         fclose($this->_file_handle);
  132.     }
  133.  
  134.     /**
  135.     * Gets information about all PPS's on the OLE container from the PPS WK's
  136.     * creates an OLE_PPS object for each one.
  137.     *
  138.     * @access private
  139.     * @param integer $pps_wk_start   Position inside the OLE file where PPS WK's start
  140.     * @param integer $big_block_size Size of big blobks in the OLE file
  141.     * @return mixed true on success, PEAR_Error on failure
  142.     */
  143.     function _readPpsWks($pps_wk_start, $big_block_size)
  144.     {
  145.         $pointer = ($pps_wk_start + 1) * $big_block_size;
  146.         while (1)
  147.         {
  148.             fseek($this->_file_handle, $pointer);
  149.             $pps_wk = fread($this->_file_handle, OLE_PPS_SIZE);
  150.             if (strlen($pps_wk) != OLE_PPS_SIZE) {
  151.                 break; // Excel likes to add a trailing byte sometimes 
  152.                 //return $this->raiseError("PPS at $pointer seems too short: ".strlen($pps_wk));
  153.             }
  154.             $name_length = unpack("c", substr($pps_wk, 64, 2)); // FIXME (2 bytes??)
  155.             $name_length = $name_length[''] - 2;
  156.             $name = substr($pps_wk, 0, $name_length);
  157.             $type = unpack("c", substr($pps_wk, 66, 1));
  158.             if (($type[''] != OLE_PPS_TYPE_ROOT) and
  159.                 ($type[''] != OLE_PPS_TYPE_DIR) and
  160.                 ($type[''] != OLE_PPS_TYPE_FILE))
  161.             {
  162.                 return $this->raiseError("PPS at $pointer has unknown type: {$type['']}");
  163.             }
  164.             $prev = unpack("V", substr($pps_wk, 68, 4));
  165.             $next = unpack("V", substr($pps_wk, 72, 4));
  166.             $dir  = unpack("V", substr($pps_wk, 76, 4));
  167.             // there is no magic number, it can take different values.
  168.             //$magic = unpack("V", strrev(substr($pps_wk, 92, 4)));
  169.             $time_1st = substr($pps_wk, 100, 8);
  170.             $time_2nd = substr($pps_wk, 108, 8);
  171.             $start_block = unpack("V", substr($pps_wk, 116, 4));
  172.             $size = unpack("V", substr($pps_wk, 120, 4));
  173.             // _data member will point to position in file!!
  174.             // OLE_PPS object is created with an empty children array!!
  175.             $this->_list[] = new OLE_PPS(null, '', $type[''], $prev[''], $next[''],
  176.                                          $dir[''], OLE::OLE2LocalDate($time_1st),
  177.                                          OLE::OLE2LocalDate($time_2nd),
  178.                                          ($start_block[''] + 1) * $big_block_size, array());
  179.             // give it a size
  180.             $this->_list[count($this->_list) - 1]->Size = $size[''];
  181.             // check if the PPS tree (starting from root) is complete
  182.             if ($this->_ppsTreeComplete(0)) {
  183.                 break;
  184.             }
  185.             $pointer += OLE_PPS_SIZE;
  186.         }
  187.     }
  188.  
  189.     /**
  190.     * It checks whether the PPS tree is complete (all PPS's read)
  191.     * starting with the given PPS (not necessarily root)
  192.     *
  193.     * @access private
  194.     * @param integer $index The index of the PPS from which we are checking
  195.     * @return boolean Whether the PPS tree for the given PPS is complete
  196.     */
  197.     function _ppsTreeComplete($index)
  198.     {
  199.         if ($this->_list[$index]->NextPps != -1) {
  200.             if (!isset($this->_list[$this->_list[$index]->NextPps])) {
  201.                 return false;
  202.             }
  203.             else {
  204.                 return $this->_ppsTreeComplete($this->_list[$index]->NextPps);
  205.             }
  206.         }
  207.         if ($this->_list[$index]->DirPps != -1) {
  208.             if (!isset($this->_list[$this->_list[$index]->DirPps])) {
  209.                 return false;
  210.             }
  211.             else {
  212.                 return $this->_ppsTreeComplete($this->_list[$index]->DirPps);
  213.             }
  214.         }
  215.         return true;
  216.     }
  217.  
  218.     /** 
  219.     * Checks whether a PPS is a File PPS or not.
  220.     * If there is no PPS for the index given, it will return false.
  221.     *
  222.     * @access public
  223.     * @param integer $index The index for the PPS
  224.     * @return bool true if it's a File PPS, false otherwise
  225.     */
  226.     function isFile($index)
  227.     {
  228.         if (isset($this->_list[$index])) {
  229.             return ($this->_list[$index]->Type == OLE_PPS_TYPE_FILE);
  230.         }
  231.         return false;
  232.     }
  233.  
  234.     /** 
  235.     * Checks whether a PPS is a Root PPS or not.
  236.     * If there is no PPS for the index given, it will return false.
  237.     *
  238.     * @access public
  239.     * @param integer $index The index for the PPS.
  240.     * @return bool true if it's a Root PPS, false otherwise
  241.     */
  242.     function isRoot($index)
  243.     {
  244.         if (isset($this->_list[$index])) {
  245.             return ($this->_list[$index]->Type == OLE_PPS_TYPE_ROOT);
  246.         }
  247.         return false;
  248.     }
  249.  
  250.     /** 
  251.     * Gives the total number of PPS's found in the OLE container.
  252.     *
  253.     * @access public
  254.     * @return integer The total number of PPS's found in the OLE container
  255.     */
  256.     function ppsTotal()
  257.     {
  258.         return count($this->_list);
  259.     }
  260.  
  261.     /**
  262.     * Gets data from a PPS
  263.     * If there is no PPS for the index given, it will return an empty string.
  264.     *
  265.     * @access public
  266.     * @param integer $index    The index for the PPS
  267.     * @param integer $position The position from which to start reading
  268.     *                          (relative to the PPS)
  269.     * @param integer $length   The amount of bytes to read (at most)
  270.     * @return string The binary string containing the data requested
  271.     */
  272.     function getData($index, $position, $length)
  273.     {
  274.         // if position is not valid return empty string
  275.         if (!isset($this->_list[$index]) or ($position >= $this->_list[$index]->Size) or ($position < 0)) {
  276.             return '';
  277.         }
  278.         // Beware!!! _data member is actually a position
  279.         fseek($this->_file_handle, $this->_list[$index]->_data + $position);
  280.         return fread($this->_file_handle, $length);
  281.     }
  282.     
  283.     /**
  284.     * Gets the data length from a PPS
  285.     * If there is no PPS for the index given, it will return 0.
  286.     *
  287.     * @access public
  288.     * @param integer $index    The index for the PPS
  289.     * @return integer The amount of bytes in data the PPS has
  290.     */
  291.     function getDataLength($index)
  292.     {
  293.         if (isset($this->_list[$index])) {
  294.             return $this->_list[$index]->Size;
  295.         }
  296.         return 0;
  297.     }
  298.  
  299.     /**
  300.     * Utility function to transform ASCII text to Unicode
  301.     *
  302.     * @access public
  303.     * @static
  304.     * @param string $ascii The ASCII string to transform
  305.     * @return string The string in Unicode
  306.     */
  307.     function Asc2Ucs($ascii)
  308.     {
  309.         $rawname = '';
  310.         for ($i = 0; $i < strlen($ascii); $i++) {
  311.             $rawname .= $ascii{$i}."\x00";
  312.         }
  313.         return $rawname;
  314.     }
  315.  
  316.     /**
  317.     * Utility function
  318.     * Returns a string for the OLE container with the date given
  319.     *
  320.     * @access public
  321.     * @static
  322.     * @param integer $date A timestamp 
  323.     * @return string The string for the OLE container
  324.     */
  325.     function LocalDate2OLE($date = null)
  326.     {
  327.         if (!isset($date)) {
  328.             return "\x00\x00\x00\x00\x00\x00\x00\x00";
  329.         }
  330.  
  331.         // factor used for separating numbers into 4 bytes parts
  332.         $factor = pow(2,32);
  333.  
  334.         // days from 1-1-1601 until the beggining of UNIX era
  335.         $days = 134774;
  336.         // calculate seconds
  337.         $big_date = $days*24*3600 + gmmktime(date("H",$date),date("i",$date),date("s",$date),
  338.                                              date("m",$date),date("d",$date),date("Y",$date));
  339.         // multiply just to make MS happy
  340.         $big_date *= 10000000;
  341.  
  342.         $high_part = floor($big_date/$factor);
  343.         // lower 4 bytes
  344.         $low_part = floor((($big_date/$factor) - $high_part)*$factor);
  345.  
  346.         // Make HEX string
  347.         $res = '';
  348.  
  349.         for ($i=0; $i<4; $i++)
  350.         {
  351.             $hex = $low_part % 0x100;
  352.             $res .= pack('c', $hex);
  353.             $low_part /= 0x100;
  354.         }
  355.         for ($i=0; $i<4; $i++)
  356.         {
  357.             $hex = $high_part % 0x100;
  358.             $res .= pack('c', $hex);
  359.             $high_part /= 0x100;
  360.         }
  361.         return $res;
  362.     }
  363.  
  364.     /**
  365.     * Returns a timestamp from an OLE container's date
  366.     *
  367.     * @access public
  368.     * @static
  369.     * @param integer $string A binary string with the encoded date
  370.     * @return string The timestamp corresponding to the string
  371.     */
  372.     function OLE2LocalDate($string)
  373.     {
  374.         if (strlen($string) != 8) {
  375.             return new PEAR_Error("Expecting 8 byte string");
  376.         }
  377.  
  378.         // factor used for separating numbers into 4 bytes parts
  379.         $factor = pow(2,32);
  380.         $high_part = 0;
  381.         for ($i=0; $i<4; $i++)
  382.         {
  383.             $al = unpack('C', $string{(7 - $i)});
  384.             $high_part += $al[''];
  385.             if ($i < 3) {
  386.                 $high_part *= 0x100;
  387.             }
  388.         }
  389.         $low_part = 0;
  390.         for ($i=4; $i<8; $i++)
  391.         {
  392.             $al = unpack('C', $string{(7 - $i)});
  393.             $low_part += $al[''];
  394.             if ($i < 7) {
  395.                 $low_part *= 0x100;
  396.             }
  397.         }
  398.         $big_date = ($high_part*$factor) + $low_part;
  399.         // translate to seconds
  400.         $big_date /= 10000000;
  401.         
  402.         // days from 1-1-1601 until the beggining of UNIX era
  403.         $days = 134774;
  404.         
  405.         // translate to seconds from beggining of UNIX era
  406.         $big_date -= $days*24*3600;
  407.         return floor($big_date);
  408.     }
  409. }
  410. ?>
  411.